#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <net/if.h>
#include <sys/select.h>
#include <sys/time.h>
#include <pthread.h>
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
// 全局数据类型定义
#define ElemType int
#define ElemTypes char
#define ERRLOG(errmsg)                                       \
    do                                                       \
    {                                                        \
        printf("%s--%s(%d):", __FILE__, __func__, __LINE__); \
        perror(errmsg);                                      \
        exit(-1);                                            \
    } while (0)
// 创建客户端的套接字
ElemType sock_set = 0;
// 接收信息
void *recv_msg(void *arg);
// 线程A
void *Thread_A(void *arg);
// 主函数
ElemType main(int argc, const char *argv[])
{
    // 检测命令行参数个数
    if (4 != argc)
    {
        printf("Usage : %s <服务器IP地址> <服务器接口号> <客户端端口号>\n", argv[0]);
        exit(-1);
    }
    // 获取客户端端口号
    sock_set = atoi(argv[3]);
    printf("%d\n", sock_set);
    // 打印欢迎信息
    printf("---欢迎使用聊天系统---\n");
    // 创建套接字
    ElemType sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == sockfd)
        ERRLOG("套接字创建失败");
    printf("---创建套接字成功---\n");
    // 填充服务器网络信息结构体
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    // 端口号
    server_addr.sin_port = htons(atoi(argv[2]));
    // IP地址
    server_addr.sin_addr.s_addr = inet_addr(argv[1]);
    if (INADDR_NONE == server_addr.sin_addr.s_addr)
        ERRLOG("IP地址转换失败");
    // 结构体长度
    socklen_t server_addr_len = sizeof(server_addr);
    printf("---填充服务器网络信息结构体成功---\n");
    // 与服务器建立连接
    if (-1 == connect(sockfd, (struct sockaddr *)&server_addr, server_addr_len))
        ERRLOG("连接错误");
    printf("---连接服务器成功---\n");
    ElemTypes buff[128] = {0};
    ElemType X = 0;
    ElemTypes buff1[128] = {0};
    while (1)
    {
        if (X == 0)
        {
            printf("\n-------------------------\n");
            printf("输入你的选择：\n1.开启群聊功能\n2.开启私聊系统\n");
            scanf("%d", &X);
        }
        pthread_t ThreadA;
        if (X == 2)
        {
            pthread_create(&ThreadA, NULL, Thread_A, NULL);
            pthread_join(ThreadA, NULL);
            pthread_mutex_destroy(&lock);
            X = 0;
        }
        if (X == 1)
        {
            if (X == 3)
            {
                memset(buff, 0, sizeof(buff));
                scanf("%s", buff);
                printf("请输入要发送的信息: ");
                ElemType ret, ret1 = 0;
                if (0 > (ret = send(sockfd, buff, sizeof(buff), 0)))
                    ERRLOG("发送信息失败");
                if (0 == strcmp(buff, "exit"))
                {
                    printf("退出群聊系统\n");
                    X = 0;
                }
                if (0 > (ret = recv(sockfd, buff, sizeof(buff1), 0)))
                    ERRLOG("接收数据失败");
                memset(buff1, 0, sizeof(buff1));
                printf("\n-------------------------\n");
                printf("收到广播消息:[%s]\n", buff);
                printf("\n-------------------------\n");
                printf("请输入下一条消息, 输入exit退出聊天系统\n");
            }
            printf("注册群聊: yes\n");
            scanf("%s", buff);
            if (0 != strcmp(buff, "yes"))
            {
                if (0 == strcmp(buff, "exit"))
                {
                    printf("退出群聊系统\n");
                    X = 0;
                }
                memset(buff, 0, sizeof(buff));
                printf("输入错误\n");
                continue;
            }
            ElemType ret, ret1 = 0;
            if (0 > (ret = send(sockfd, buff, sizeof(buff), 0)))
                ERRLOG("发送失败");
            memset(buff1, 0, sizeof(buff1));
            printf("\n-------------------------\n");
            printf("请输入下一条消息, 输入exit退出聊天系统\n");
            memset(buff, 0, sizeof(buff));
            printf("您的端口号: %s", argv[3]);
            printf("\n----------\n");
            printf("请输入要发送的端口号: ");
            scanf("%s", buff);
            ret, ret1 = 0;
            if (0 > (ret = send(sockfd, buff, sizeof(buff), 0)))
                ERRLOG("发送端口号失败");
            if (0 == strcmp(buff, "exit"))
            {
                printf("退出群聊系统\n");
                X = 0;
            }
            printf("\n-------------------------\n");
            printf("请输入下一条消息, 输入exit退出聊天系统\n");
            X = 3;
        }
    }
    // 关闭套接字
    close(sockfd);
    printf("---关闭套接字成功---\n");
    return 0;
}
// 接收信息
void *recv_msg(void *arg)
{
    ElemType ret = 0;
    // 通信通道socket
    ElemType *socket_fd = (ElemType *)arg;
    // 存储信息发送方的IP地址信息
    struct sockaddr_in src_addr = {0};
    // 地址大小
    ElemType len = sizeof(src_addr);
    // 信息缓冲区
    ElemTypes msg[1024] = {0};
    // 循环接收发来的数据
    while (1)
    {
        ret = recvfrom(*socket_fd, msg, sizeof(msg), 0, (struct sockaddr *)&src_addr, &len);
        if (ret == -1)
            break;
        // 输出信息发送方IP地址与端口号
        printf("\n-------------------------\n");
        printf("\n来自[%s:%d]", inet_ntoa(src_addr.sin_addr), ntohs(src_addr.sin_port));
        printf("传输信息%s\n", msg);
        printf("\n-------------------------\n");
        printf("请输入下一条消息, 输入exit退出私聊系统:");
        // 处理信息
        // 退出私聊系统
        if (strcmp(msg, "exit") == 0)
        {
            printf("退出私聊系统\n");
            // 告知主线程
            break;
        }
        // 清空缓存
        memset(msg, 0, sizeof(msg));
    }
    // 关闭通信通道socket
    close(*socket_fd);
    return NULL;
}
// 线程A
void *Thread_A(void *arg)
{
    pthread_mutex_lock(&lock);
    printf("线程A开始运行\n");
    // 创建UDP通信通道socket
    ElemType UDP_socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (UDP_socket_fd < 0)
    {
        perror("套接字创建失败\n");
        return NULL;
    }
    // 设置UDP地址并绑定
    struct sockaddr_in local_addr;
    // 初始化服务器地址
    bzero(&local_addr, sizeof(local_addr));
    // IPV4协议
    local_addr.sin_family = AF_INET;
    // 设置发送方端口号
    local_addr.sin_port = htons(sock_set);
    // 系统检测本地网卡， 自动绑定本地IP
    local_addr.sin_addr.s_addr = INADDR_ANY;
    ElemType ret = bind(UDP_socket_fd, (struct sockaddr *)&local_addr, sizeof(local_addr));
    if (ret < 0)
    {
        perror("失败绑定本地地址\n");
        close(UDP_socket_fd);
        return NULL;
    }
    // 开启接收线程
    pthread_t recv_thread;
    pthread_create(&recv_thread, NULL, recv_msg, (void *)&UDP_socket_fd);
    // 设置目的IP地址
    struct sockaddr_in dest_addr = {0};
    // IPV4协议
    dest_addr.sin_family = AF_INET;
    // 目的端口号
    ElemType dest_port = 0;
    // 目的IP地址
    ElemTypes dest_IP[32] = {0};
    // 信息缓冲区
    ElemTypes msg[1024] = {0};
    // 循环发送信息
    while (1)
    {
        printf("输入目的IP, 端口号, 要发信息");
        scanf("%s %d %s", dest_IP, &dest_port, msg);
        // 设置接收方端口号
        dest_addr.sin_port = htons(dest_port);
        // 设置接收方IP
        dest_addr.sin_addr.s_addr = inet_addr(dest_IP);
        sendto(UDP_socket_fd, msg, strlen(msg), 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
        if (dest_IP == NULL || dest_port == 0 || msg == NULL)
        {
            printf("操作有误，请重新输入\n");
            continue;
        }
        if (strcmp(msg, "exit") == 0)
        {
            printf("退出私聊系统\n");
            // 取消子线程
            pthread_cancel(recv_thread);
            // 退出循环
            break;
        }
        // 清空缓存
        memset(msg, 0, sizeof(msg));
        memset(dest_IP, 0, sizeof(dest_IP));
    }
    // 关闭通信通道
    close(UDP_socket_fd);
    pthread_mutex_unlock(&lock);
    return NULL;
}